package com.ruyuan.payment.server.service;

import com.ruyuan.payment.server.domain.*;
import com.ruyuan.payment.server.enums.OrderInfoStatusEnum;
import com.ruyuan.payment.server.enums.OrderPayRefundStatusEnum;
import com.ruyuan.payment.server.enums.OrderPayStatusEnum;
import com.ruyuan.payment.server.mapper.OrderInfoMapper;
import com.ruyuan.payment.server.mapper.OrderPayMapper;
import com.ruyuan.payment.server.mapper.OrderPayRefundMapper;
import com.ruyuan.payment.server.util.NumUtil;
import com.ruyuan.payment.server.util.SnowFlake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;

@Service
public class PayRefundService {

    private static final Logger LOG = LoggerFactory.getLogger(PayRefundService.class);

    @Resource
    private OrderPayMapper orderPayMapper;

    @Resource
    private OrderInfoMapper orderInfoMapper;

    @Resource
    private AccountService accountService;

    @Resource
    private OrderInfoService orderInfoService;

    @Resource
    private SnowFlake snowFlake;

    @Resource
    private OrderPayRefundMapper orderPayRefundMapper;

    public OrderPayRefund beforeRefund(OrderPay orderPayDB, BigDecimal accountAmount, BigDecimal channelAmount) {
        Date now = new Date();
        OrderPayRefund orderPayRefund = new OrderPayRefund();
        orderPayRefund.setId(snowFlake.nextId());
        orderPayRefund.setMemberId(orderPayDB.getMemberId());
        orderPayRefund.setOrderNo(orderPayDB.getOrderNo());
        orderPayRefund.setPayNo(orderPayDB.getPayNo());
        orderPayRefund.setRefundNo(NumUtil.genRefundNo());
        orderPayRefund.setRefundTime(now);
        orderPayRefund.setAmount(accountAmount.add(channelAmount));
        orderPayRefund.setAccountAmount(accountAmount);
        orderPayRefund.setChannelAmount(channelAmount);
        orderPayRefund.setChannel(orderPayDB.getChannel());
        orderPayRefund.setStatus(OrderPayRefundStatusEnum.INIT.getCode());
        orderPayRefund.setCreatedTime(now);
        orderPayRefund.setUpdatedTime(now);
        orderPayRefundMapper.insert(orderPayRefund);
        return orderPayRefund;
    }

    /**
     * 计算累计退款金额
     * 修改退款表为成功
     * 修改支付订单为已退款或部分退款
     * 修改商品订单为已退款或部分退款
     * 如果本次涉及余额退款，则退回余额
     * 通知业务系统退款成功
     * @param orderPayDB
     * @param orderPayRefundDB
     */
    public void refund(OrderPay orderPayDB, OrderPayRefund orderPayRefundDB) {
        LOG.info("本地退款逻辑开始：orderPayDB：{}", orderPayDB);
        Date now = new Date();

        // 累计退款金额 = 已退款金额 + 本次退款金额
        BigDecimal totalRefundAmount = orderPayDB.getRefundAmount().add(orderPayRefundDB.getAmount());

        // 修改退款表为成功
        orderPayRefundDB.setStatus(OrderPayRefundStatusEnum.SUCCESS.getCode());
        orderPayRefundDB.setUpdatedTime(now);
        orderPayRefundMapper.updateByPrimaryKey(orderPayRefundDB);

        orderPayDB.setRefundAmount(totalRefundAmount);
        if (!updateOrderPay(orderPayDB)) {
            return;
        }

        OrderInfo orderInfoDB = orderInfoService.selectByOrderNo(orderPayDB.getOrderNo());
        orderInfoDB.setStatus(orderPayDB.getStatus());
        if (!updateOrderInfo(orderInfoDB)) {
            return;
        }

        if (orderPayRefundDB.getAccountAmount().compareTo(BigDecimal.ZERO) > 0) {
            accountService.doAccount(orderPayRefundDB.getMemberId(), orderPayRefundDB.getAccountAmount(), "发生退款，支付订单号：" + orderPayDB.getPayNo());
        }

        // TODO 通知业务系统
    }

    private boolean updateOrderPay(OrderPay orderPayDB) {
        Date now = new Date();
        if (orderPayDB.getRefundAmount().compareTo(orderPayDB.getAmount()) < 0) {
            orderPayDB.setStatus(OrderPayStatusEnum.PART_REFUND.getCode());
        } else {
            orderPayDB.setStatus(OrderPayStatusEnum.REFUND.getCode());
        }
        String statusDesc = OrderPayStatusEnum.getDesc(orderPayDB.getStatus());
        LOG.info("更新支付订单为{}：orderPayDB：{}", statusDesc, orderPayDB);
        orderPayDB.setUpdatedTime(now);
        OrderPayExample orderPayExample = new OrderPayExample();
        // id = xxx and status = 'S'
        // id = xxx and (status = 'S' or status = 'A')
        // (id = xxx and status = 'S') or (id = xxx and status = 'A')
        orderPayExample.or().andIdEqualTo(orderPayDB.getId())
                .andStatusEqualTo(OrderPayStatusEnum.SUCCESS.getCode());
        orderPayExample.or().andIdEqualTo(orderPayDB.getId())
                .andStatusEqualTo(OrderPayStatusEnum.PART_REFUND.getCode());
        int i = orderPayMapper.updateByExample(orderPayDB, orderPayExample);
        if (i == 0) {
            LOG.info("防重拦截：支付订单状态非成功或非部分退款：payNo：{}，status：{}", orderPayDB.getPayNo(), orderPayDB.getStatus());
            return false;
        }
        return true;
    }

    private boolean updateOrderInfo(OrderInfo orderInfoDB) {
        Date now = new Date();
        orderInfoDB.setUpdatedAt(now);
        OrderInfoExample orderInfoExample = new OrderInfoExample();
        orderInfoExample.or().andIdEqualTo(orderInfoDB.getId())
                .andStatusEqualTo(OrderInfoStatusEnum.SUCCESS.getCode());
        orderInfoExample.or().andIdEqualTo(orderInfoDB.getId())
                .andStatusEqualTo(OrderInfoStatusEnum.PART_REFUND.getCode());
        String statusDesc = OrderPayStatusEnum.getDesc(orderInfoDB.getStatus());
        LOG.info("更新商品订单为{}：orderInfoDB：{}", statusDesc, orderInfoDB);
        int i = orderInfoMapper.updateByExample(orderInfoDB, orderInfoExample);
        if (i == 0) {
            LOG.info("防重拦截：商品订单状态非成功或非部分退款：orderNo：{}，status：{}", orderInfoDB.getOrderNo(), orderInfoDB.getStatus());
            return false;
        }
        return true;
    }
}
